package com.haredot.websocket;

import com.alibaba.fastjson2.JSONObject;
import com.haredot.entity.Message;
import com.haredot.entity.Notify;
import com.haredot.event.RssMessageEvent;
import com.haredot.event.RssNotifyEvent;
import com.haredot.event.enums.NotifyType;
import com.haredot.utils.ApplicationUtils;
import com.haredot.vo.MessageVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 每一次新注入的客户端 都是一个全新的 ChatWebSocketHandler 对象，
 *  索引 onlineMap 必需是 一个 static 修饰的，被所有对象共享才可以！
 */
@Slf4j
@ServerEndpoint("/chat/{userId}")
public class ChatWebSocketHandler  {
    /**
     * 存储在线人数
     */
    private final static Map<Long, Session> onlineMap = new ConcurrentHashMap<>();

    @OnOpen
    public  void onOpen(Session session, @PathParam("userId") Long userId){
        // 根据 手机号查询 对应的 ID 信息
        onlineMap.put(userId, session) ;
    }

    @OnMessage
    public void onMessage(String messageText, Session mySession) throws IOException {
        log.debug("接收到的聊天信息是：" + messageText);
        // 消息格式 (JSON *  { fromUser ,  toUser ,  content  })
        Message message = JSONObject.parseObject(messageText, MessageVo.class);

        // 去掉毫秒数 (否则 会导致 存储到 redis中的时间 和 mysql数据库中的时间 相差 1秒 ， 数据库不存储毫秒，会进行四舍五入)
        //message.setCreateTime(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS));
        message.setCreateTime(LocalDateTime.now());
        // 接收用户发送的消息。将消息 以键 chat:fromuser:touser 格式写入到 redis数据库 有序集合中， score 为 写入的时间
        // 将消息写入到数据库表中 (未读状态)
        // 通过 Spring 事件监听 发送 将动作 发送到 RssMessageEvent 事件中
        ApplicationEventPublisher applicationEventPublisher = ApplicationUtils.getApplicationEventPublisher();
        applicationEventPublisher.publishEvent(new RssMessageEvent(this, message));

        // 如果当前消息没有 ID , 说明该消息没有被存储到数据库，代表该用户可能被对方拉入了黑名单中
        if (message.getId() == null) {
            mySession.getBasicRemote().sendText("403");
            return ;
        }
        // 推送私信 通知
        Notify notify = Notify.builder()
                .text("您有新的聊天信息")
                .typeName(NotifyType.LETTER.getTypeName())
                .userId(message.getToUser())
                .createTime(LocalDateTime.now())
                .build();
        applicationEventPublisher.publishEvent(new RssNotifyEvent(this, notify));
        // 根据消息中的 toUser 找到 对应的 好友，
        Long toUser = message.getToUser();
        Session session = this.onlineMap.get(toUser);
        // 查看 好友是否在线、 如果在线、推送消息给 好友 ~
        String messageContent = JSONObject.toJSONString(message) ;
        if (session != null) {
            session.getBasicRemote().sendText(messageContent);
        }
        // 消息发给自己一份
        mySession.getBasicRemote().sendText(messageContent);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        Long key = null ;
        for(Map.Entry<Long, Session> entry : this.onlineMap.entrySet()) {

            if (entry.getValue() == session) {
                key = entry.getKey() ;
                break ;
            }
        }
        if (key != null)  this.onlineMap.remove(key) ;
    }
}
